package com.luanmi.launcherdemo

import android.Manifest
import android.app.Activity
import android.app.ActivityManager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.os.SystemProperties
import android.provider.Settings
import android.util.Log
import android.view.View
import android.webkit.WebView
import android.widget.EditText
import android.widget.Switch
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat
import androidx.core.content.ContextCompat
import com.luanmi.launcherdemo.frp.ShellService
import java.io.File
import kotlin.random.Random

class MainActivity : AppCompatActivity() {
    companion object{
        var rotation = 3
    }
    private lateinit var  tv_ip :TextView
    private lateinit var  tv_log :TextView
    private lateinit var  state_switch : Switch
    private lateinit var  et_port : EditText
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tv_ip = findViewById(R.id.tv_ip)
        state_switch = findViewById(R.id.state_switch)
        et_port = findViewById(R.id.et_port)
        tv_log = findViewById(R.id.tv_log)

        mBound = isServiceRunning(ShellService::class.java)
        state_switch = findViewById(R.id.state_switch)
        state_switch.isChecked = mBound
        state_switch.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) (remoteConnect()) else (stopShell()) }
//        state_switch.setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) (startShell()) else (stopShell()) }

        checkNotificationPermission()
        createBGNotificationChannel()
        checkConfig()

    }


    fun getIp(view: View) {
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        var ipString = ""
        connectivityManager.getLinkProperties(connectivityManager.activeNetwork)?.addresses?.forEach {
            ipString+= " ${it.hostAddress}\n"
        }
        tv_ip.text = "$ipString"

        val port = SystemProperties.get("service.adb.tcp.port")
        if(port!="5555"){
            Settings.Global.putInt(contentResolver, Settings.Global.ADB_ENABLED, 1)
            SystemProperties.set("sys.connect.adb.wiress","1")
            SystemProperties.set("service.adb.tcp.port","5555")

//            SystemProperties.set("persist.sys.usb.config","adb")
//            sys.connect.adb.wiress=1
//            setprop service.adb.tcp.port 5555

        }else{
            SystemProperties.set("sys.connect.adb.wiress","0")
            SystemProperties.set("service.adb.tcp.port","")
//            SystemProperties.set("persist.sys.usb.config","")
            Settings.Global.putInt(contentResolver, Settings.Global.ADB_ENABLED, 0)
            tv_ip.text = ""
        }
    }

    private fun rotationSystem(rotation: Int){
        Settings.System.putInt(
            contentResolver,
            Settings.System.USER_ROTATION,
            rotation
        )

        val accelerometerRotation =
            Settings.System.getInt(contentResolver, Settings.System.ACCELEROMETER_ROTATION)
        if (accelerometerRotation != 0) {
            Settings.System.putInt(
                contentResolver,
                Settings.System.ACCELEROMETER_ROTATION,
                0
            )
        }
    }

    fun rotationScreen(view: View) {
        rotationSystem(rotation)
        rotation ++
        if(rotation > 3){
            rotation = 0
        }
    }

    fun remoteConnect() {
        openAdb()
        tv_ip.postDelayed({
            startShell()
        },2500)

    }

    fun openAdb() {
        val connectivityManager = getSystemService(ConnectivityManager::class.java)
        var ipString = ""
        connectivityManager.getLinkProperties(connectivityManager.activeNetwork)?.addresses?.forEach {
            ipString+= " ${it.hostAddress}\n"
        }
        tv_ip.text = "$ipString"

        val port = SystemProperties.get("service.adb.tcp.port")
        openPort(port)

        tv_ip.postDelayed({
            val port = SystemProperties.get("service.adb.tcp.port")
            openPort(port)
        },500)

        tv_ip.postDelayed({
            val port = SystemProperties.get("service.adb.tcp.port")
            openPort(port)
        },1000)

        tv_ip.postDelayed({
            val port = SystemProperties.get("service.adb.tcp.port")
            if(port == "5555"){
                return@postDelayed
            }
            openPort(port)
        },1500)

        tv_ip.postDelayed({
            val port = SystemProperties.get("service.adb.tcp.port")
            if(port == "5555"){
                return@postDelayed
            }
            openPort(port)
        },2000)

    }

    private fun openPort(port:String){
        if(port!="5555"){
            Settings.Global.putInt(contentResolver, Settings.Global.ADB_ENABLED, 1)
            SystemProperties.set("sys.connect.adb.wiress","1")
            SystemProperties.set("service.adb.tcp.port","5555")

        }else{
            SystemProperties.set("sys.connect.adb.wiress","0")
            SystemProperties.set("service.adb.tcp.port","")
//            SystemProperties.set("persist.sys.usb.config","")
            Settings.Global.putInt(contentResolver, Settings.Global.ADB_ENABLED, 0)
            tv_ip.text = ""
        }
    }







    private lateinit var mService: ShellService
    private var mBound: Boolean = false

    /** Defines callbacks for service binding, passed to bindService()  */
    private val connection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            val binder = service as ShellService.LocalBinder
            mService = binder.getService()
            mBound = true
            state_switch.isChecked = true
            mService.setCallBack {
                tv_log.post {
                    tv_log.text = it
                }
            }
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            mBound = false
            state_switch.isChecked = false
        }
    }

    private fun startShell() {
        val remotePort = et_port.text.toString().toInt()
        setConfig("远程调试-${Random.nextInt(1000)}",5555,remotePort)
        val intent = Intent(this, ShellService::class.java)

        val dirPath = getExternalFilesDir("frp")
        val soFile = File(dirPath,"frpc.so")
        val configFile = File(dirPath,"config.toml")

        if(!soFile.exists()){
            state_switch.isChecked = false
            return
        }

        intent.putExtra("filename", "${soFile.absolutePath}")
        intent.putExtra("configPath", "${configFile.absolutePath}")
        startService(intent)
        // Bind to LocalService
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    private fun stopShell() {
        val intent = Intent(this, ShellService::class.java)
        unbindService(connection)
        stopService(intent)
    }

    private fun createBGNotificationChannel() {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is new and not in the support library
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "远程调试后台服务"
            val descriptionText = "远程调试后台服务通知"
            val importance = NotificationManager.IMPORTANCE_MIN
            val channel = NotificationChannel("远程调试", name, importance).apply {
                description = descriptionText
            }
            // Register the channel with the system
            val notificationManager: NotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    private fun isServiceRunning(serviceClass: Class<*>): Boolean {
        val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        for (service in manager.getRunningServices(Int.MAX_VALUE)) {
            if (serviceClass.name == service.service.className) {
                return true
            }
        }
        return false
    }

    fun setConfig(name:String,localPort:Int,remotePort:Int) {
      val configStr =  """
            serverAddr = "123.207.46.195"
            serverPort = 50000
            auth.token = "boby"
            [log]
            level = "debug"
            disablePrintColor = true

            [[proxies]]
            name = "$name"
            type = "tcp"
            localIP = "localhost"
            localPort = $localPort
            remotePort = $remotePort
            
        """.trimIndent()
        val dirPath = getExternalFilesDir("frp")
        if(!dirPath.exists()){
            dirPath.mkdirs()
        }
        val configFile = File(dirPath,"config.toml")
        configFile.outputStream().use {
            it.write(configStr.toByteArray())
        }

    }
    fun checkConfig() {

//        val dirPath = getExternalFilesDir("frp")
        val dirPath = File(filesDir,"frp")
//        Log.d("adx", listFiles.joinToString(","))
        if(!dirPath.exists()){
            dirPath.mkdirs()
        }
        val soFile = File(dirPath,"frpc.so")
        if(!soFile.exists()){
            val soFileTemp = File(dirPath,"frpc.so_back")
            soFileTemp.createNewFile()
            val assetmanager = resources.assets
            soFileTemp.outputStream().use {
                it.write(assetmanager.open(("frpc.so")).readBytes())
            }
            soFileTemp.setExecutable(true)
            soFileTemp.renameTo(soFile)
        }
        var executable = soFile.setExecutable(true)
        Log.d("executable","set executable $executable path:${soFile.absolutePath}")
    }
    private fun checkNotificationPermission() {
        val requestPermissionLauncher = registerForActivityResult(
            ActivityResultContracts.RequestPermission()
        ) { isGranted: Boolean ->
            if (isGranted) {
                // Permission is granted. Continue the action or workflow in your
                // app.
            } else {
                // Explain to the user that the feature is unavailable because the
                // feature requires a permission that the user has denied. At the
                // same time, respect the user's decision. Don't link to system
                // settings in an effort to convince the user to change their
                // decision.
            }
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(
                    this, Manifest.permission.POST_NOTIFICATIONS
                ) != PackageManager.PERMISSION_GRANTED
            ) {
                requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
            }
        }
    }

    fun clearLog(view: View) {
        tv_log.text = ""
        mService.clearOutput()
    }
}
